package com.google.code.pomhelper;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.List;

import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import com.google.code.pomhelper.graph.ModulNode;
import com.google.code.pomhelper.graph.Tokens;

/**
 * Print all declared modules including submodules.
 * 
 * @author rm
 * 
 */

@Mojo(name = "print-modules")
public class PrintModulesMojo extends AbstractMojo {

	/**
	 * The Maven project to analyze.
	 */
	@Component
	private MavenProject project;

	/**
	 * Skip plugin execution completely.
	 */
	@Parameter(property = "print-modules.skip", defaultValue = "false")
	private boolean skip;

	/**
	 * Sort modules alphabetical.
	 */
	@Parameter(property = "print-modules.sort", defaultValue = "false")
	private boolean sort;

	private ModulNode node;

	private int depth = 0;

	private final Tokens tokens = Tokens.STANDARD_TOKENS;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		if (isSkip()) {
			getLog().info("Skipping plugin execution");
			return;
		}

		node = new ModulNode(null, project.getArtifactId(), project.getGroupId(), project.getModel(), ".");

		@SuppressWarnings("unchecked")
		List<String> modules = project.getModules();

		if (getLog().isInfoEnabled() && modules != null && modules.size() > 0) {

			List<String> mods = project.getModules();

			if (mods != null) {
				for (String mod : mods) {
					ModulNode subNode = new ModulNode(node, mod);
					node.getChildren().add(subNode);
				}
			}

			for (ModulNode n : node.getChildren()) {
				visitModule(n, n.getModulName());
			}

			getLog().info(node.toNodeString());

			printModules(node);
		}
	}

	private void printModules(ModulNode node) {
		depth++;
		if (node.getChildren() != null) {
			for (ModulNode currentNode : node.getChildren()) {
				StringBuilder sb = new StringBuilder();

				for (int i = 1; i < depth; i++) {
					sb.append(tokens.getFillIndent(isLast(currentNode, i)));
				}

				if (depth > 0) {
					sb.append(tokens.getNodeIndent(isLast(currentNode)));
				}

				sb.append(currentNode.toNodeString());

				getLog().info(sb.toString());
				printModules(currentNode);
			}
		}
		depth--;
	}

	private boolean isLast(ModulNode node, int ancestorDepth) {
		int distance = depth - ancestorDepth;

		while (distance-- > 0) {
			node = node.getParent();
		}

		return isLast(node);
	}

	private boolean isLast(ModulNode currentNode) {
		ModulNode parent = currentNode.getParent();

		boolean last;

		if (parent == null) {
			last = true;
		} else {
			List<ModulNode> siblings = parent.getChildren();

			last = (siblings.indexOf(currentNode) == siblings.size() - 1);
		}

		return last;
	}

	private void visitModule(ModulNode node, String moduleName) {
		String pomPath = node.getParent().getPomPath() + File.separator + node.getModulName();

		MavenXpp3Reader pomReader = new MavenXpp3Reader();
		Reader reader = null;

		try {
			File file = new File(pomPath + File.separator + "pom.xml");
			node.setPomPath(file.getCanonicalFile().getParent());

			reader = ReaderFactory.newXmlReader(file);

			Model model = pomReader.read(reader);

			node.setModel(model);

			node.setArtifactId(model.getArtifactId());

			if (StringUtils.isNotEmpty(model.getGroupId())) {
				node.setGroupId(model.getGroupId());
			} else {
				node.setGroupId(model.getParent().getGroupId());
			}

			List<String> mods = model.getModules();

			if (mods != null) {
				for (String mod : mods) {
					ModulNode subNode = new ModulNode(node, mod);
					node.getChildren().add(subNode);
				}
			}

			for (ModulNode n : node.getChildren()) {
				visitModule(n, n.getModulName());
			}

		} catch (IOException e) {
			getLog().error(e);
		} catch (XmlPullParserException e) {
			getLog().error(e);
		}
	}

	/**
	 * @return the skip
	 */
	public boolean isSkip() {
		return skip;
	}

	/**
	 * @param skip
	 *            the skip to set
	 */
	public void setSkip(boolean skip) {
		this.skip = skip;
	}

	/**
	 * @return the sort
	 */
	public boolean isSort() {
		return sort;
	}

	/**
	 * @param sort
	 *            the sort to set
	 */
	public void setSort(boolean sort) {
		this.sort = sort;
	}
}
